// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package traffic

import (
	"apiote.xyz/p/szczanieckiej/transformers"

	"encoding/csv"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/text/transform"
)

type ViennaWL struct {
	client http.Client
}

func (ViennaWL) getTimezone() *time.Location {
	l, _ := time.LoadLocation("Europe/Vienna")
	return l
}

func (ViennaWL) ConvertVehicles() ([]Vehicle, error) {
	return []Vehicle{}, nil
}

func (ViennaWL) GetVersions(_ time.Time, timezone *time.Location) ([]Version, error) {
	version, err := MakeVersionTimezone("00000101_99991231", timezone)
	if err != nil {
		return nil, err
	}
	version.Link = "https://www.wienerlinien.at/ogd_realtime/doku/ogd/gtfs/gtfs.zip"
	return []Version{version}, nil
}

func (ViennaWL) String() string {
	return "vienna_wl"
}

func (ViennaWL) RealtimeFeeds() map[RealtimeFeedType]string {
	return map[RealtimeFeedType]string{}
}

func (ViennaWL) Transformer() transform.Transformer {
	return transform.Chain(transformers.TransformerDE, transformers.TransformerHU, transformers.TransformerFR)
}

func (ViennaWL) Name() string {
	return "Vienna WL"
}

func (ViennaWL) Flags() FeedFlags {
	return FeedFlags{
		Headsign:     HeadsignTripHeadsing,
		StopIdFormat: "{{stop_id}}",
		StopName:     "{{stop_name}} | {{platform_code}}",
		LineName:     "{{route_short_name}}",
	}
}

func (v ViennaWL) FeedPrepareZip(path string) error {
	url := "https://www.wienerlinien.at/ogd_realtime/doku/ogd/wienerlinien-ogd-haltepunkte.csv"
	response, err := v.client.Get(url)
	if err != nil {
		return fmt.Errorf("FeedPrepareZip: cannot GET ‘%s’: %w", url, err)
	}
	rbls := map[string][]string{}
	err = forEachRowOpenedComma(response.Body, ';', func(offset int64, fields map[string]int, record []string) error {
		stopName := record[fields["StopText"]]
		rbl := rbls[stopName]
		rbl = append(rbl, record[fields["StopID"]])
		rbls[stopName] = rbl
		return nil
	})
	if err != nil {
		return fmt.Errorf("while parsing haltepunkte file: %w", err)
	}

	// NOTE add nonprintable character before umlauts
	trips2File, err := os.OpenFile(filepath.Join(path, "trips2.txt"), os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return fmt.Errorf("while opening trips2 file: %w", err)
	}
	defer trips2File.Close()
	w := csv.NewWriter(trips2File)
	err = forEachRowWithHeader(filepath.Join(path, "trips.txt"), func(header []string) error {
		err := w.Write(header)
		if err != nil {
			return fmt.Errorf("while writing header: %w", err)
		} else {
			return nil
		}
	}, func(offset int64, fields map[string]int, record []string) error {
		record[fields["trip_headsign"]] = strings.ReplaceAll(record[fields["trip_headsign"]], "ü", ZWJ+"ü")
		record[fields["trip_headsign"]] = strings.ReplaceAll(record[fields["trip_headsign"]], "Ü", ZWJ+"Ü")
		record[fields["trip_headsign"]] = strings.ReplaceAll(record[fields["trip_headsign"]], "ä", ZWJ+"ä")
		record[fields["trip_headsign"]] = strings.ReplaceAll(record[fields["trip_headsign"]], "Ä", ZWJ+"Ä")
		record[fields["trip_headsign"]] = strings.ReplaceAll(record[fields["trip_headsign"]], "ö", ZWJ+"ö")
		record[fields["trip_headsign"]] = strings.ReplaceAll(record[fields["trip_headsign"]], "Ö", ZWJ+"Ö")

		err := w.Write(record)
		if err != nil {
			return fmt.Errorf("while writing record: %w", err)
		} else {
			return nil
		}
	})
	w.Flush()
	err = os.Remove(filepath.Join(path, "trips.txt"))
	if err != nil {
		return fmt.Errorf("while removing trips: %w", err)
	}
	err = os.Rename(filepath.Join(path, "trips2.txt"), filepath.Join(path, "trips.txt"))
	if err != nil {
		return fmt.Errorf("while renaming trips: %w", err)
	}

	// NOTE add nonprintable character before umlauts and add RBL stop IDs and add platform code
	rblsByStopID := map[string]string{}
	stops2File, err := os.OpenFile(filepath.Join(path, "stops2.txt"), os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return fmt.Errorf("while opening stops2 file: %w", err)
	}
	defer stops2File.Close()
	w = csv.NewWriter(stops2File)
	err = forEachRowWithHeader(filepath.Join(path, "stops.txt"), func(header []string) error {
		header = append(header, "platform_code")
		err := w.Write(header)
		if err != nil {
			return fmt.Errorf("while writing header: %w", err)
		} else {
			return nil
		}
	}, func(offset int64, fields map[string]int, record []string) error {
		fields["platform_code"] = len(record)

		oldStopName := record[fields["stop_name"]]
		record[fields["stop_name"]] = strings.ReplaceAll(record[fields["stop_name"]], "ü", ZWJ+"ü")
		record[fields["stop_name"]] = strings.ReplaceAll(record[fields["stop_name"]], "Ü", ZWJ+"Ü")
		record[fields["stop_name"]] = strings.ReplaceAll(record[fields["stop_name"]], "ä", ZWJ+"ä")
		record[fields["stop_name"]] = strings.ReplaceAll(record[fields["stop_name"]], "Ä", ZWJ+"Ä")
		record[fields["stop_name"]] = strings.ReplaceAll(record[fields["stop_name"]], "ö", ZWJ+"ö")
		record[fields["stop_name"]] = strings.ReplaceAll(record[fields["stop_name"]], "Ö", ZWJ+"Ö")

		oldStopID := record[fields["stop_id"]]
		newStopID := oldStopID + "|" + strings.Join(rbls[oldStopName], ",")
		rblsByStopID[oldStopID] = newStopID

		record[fields["stop_id"]] = newStopID

		stopSplit := strings.Split(oldStopID, ":")
		if len(stopSplit) >= 5 {
			record = append(record, stopSplit[4])
		}

		err := w.Write(record)
		if err != nil {
			return fmt.Errorf("while writing record: %w", err)
		} else {
			return nil
		}
	})
	w.Flush()
	err = os.Remove(filepath.Join(path, "stops.txt"))
	if err != nil {
		return fmt.Errorf("while removing stops: %w", err)
	}
	err = os.Rename(filepath.Join(path, "stops2.txt"), filepath.Join(path, "stops.txt"))
	if err != nil {
		return fmt.Errorf("while renaming stops: %w", err)
	}

	// NOTE add RBL stop IDs
	stopTimes2File, err := os.OpenFile(filepath.Join(path, "stop_times2.txt"), os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return fmt.Errorf("while opening stop_times2 file: %w", err)
	}
	defer stopTimes2File.Close()
	w = csv.NewWriter(stopTimes2File)
	err = forEachRowWithHeader(filepath.Join(path, "stop_times.txt"), func(header []string) error {
		err := w.Write(header)
		if err != nil {
			return fmt.Errorf("while writing header: %w", err)
		} else {
			return nil
		}
	}, func(offset int64, fields map[string]int, record []string) error {
		record[fields["stop_id"]] = rblsByStopID[record[fields["stop_id"]]]
		err := w.Write(record)
		if err != nil {
			return fmt.Errorf("while writing record: %w", err)
		} else {
			return nil
		}
	})
	w.Flush()
	err = os.Remove(filepath.Join(path, "stop_times.txt"))
	if err != nil {
		return fmt.Errorf("while removing stop_times: %w", err)
	}
	err = os.Rename(filepath.Join(path, "stop_times2.txt"), filepath.Join(path, "stop_times.txt"))
	if err != nil {
		return fmt.Errorf("while renaming stop_times: %w", err)
	}

	// NOTE add nonprintable character before umlauts
	routes2File, err := os.OpenFile(filepath.Join(path, "routes2.txt"), os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return fmt.Errorf("while opening routes2 file: %w", err)
	}
	defer routes2File.Close()
	w = csv.NewWriter(routes2File)
	err = forEachRowWithHeader(filepath.Join(path, "routes.txt"), func(header []string) error {
		err := w.Write(header)
		if err != nil {
			return fmt.Errorf("while writing header: %w", err)
		} else {
			return nil
		}
	}, func(offset int64, fields map[string]int, record []string) error {
		record[fields["route_short_name"]] = strings.ReplaceAll(record[fields["route_short_name"]], "ü", ZWJ+"ü")
		record[fields["route_short_name"]] = strings.ReplaceAll(record[fields["route_short_name"]], "Ü", ZWJ+"Ü")
		record[fields["route_short_name"]] = strings.ReplaceAll(record[fields["route_short_name"]], "ä", ZWJ+"ä")
		record[fields["route_short_name"]] = strings.ReplaceAll(record[fields["route_short_name"]], "Ä", ZWJ+"Ä")
		record[fields["route_short_name"]] = strings.ReplaceAll(record[fields["route_short_name"]], "ö", ZWJ+"ö")
		record[fields["route_short_name"]] = strings.ReplaceAll(record[fields["route_short_name"]], "Ö", ZWJ+"Ö")
		record[fields["route_long_name"]] = strings.ReplaceAll(record[fields["route_long_name"]], "ü", ZWJ+"ü")
		record[fields["route_long_name"]] = strings.ReplaceAll(record[fields["route_long_name"]], "Ü", ZWJ+"Ü")
		record[fields["route_long_name"]] = strings.ReplaceAll(record[fields["route_long_name"]], "ä", ZWJ+"ä")
		record[fields["route_long_name"]] = strings.ReplaceAll(record[fields["route_long_name"]], "Ä", ZWJ+"Ä")
		record[fields["route_long_name"]] = strings.ReplaceAll(record[fields["route_long_name"]], "ö", ZWJ+"ö")
		record[fields["route_long_name"]] = strings.ReplaceAll(record[fields["route_long_name"]], "Ö", ZWJ+"Ö")

		err := w.Write(record)
		if err != nil {
			return fmt.Errorf("while writing record: %w", err)
		} else {
			return nil
		}
	})
	w.Flush()
	err = os.Remove(filepath.Join(path, "routes.txt"))
	if err != nil {
		return fmt.Errorf("while removing routes: %w", err)
	}
	err = os.Rename(filepath.Join(path, "routes2.txt"), filepath.Join(path, "routes.txt"))
	if err != nil {
		return fmt.Errorf("while renaming routes: %w", err)
	}

	return nil
}

func (ViennaWL) QRInfo() (string, QRLocation, string) {
	return "", QRLocationNone, ""
}
